# Purpose: Defines custom exception types for the application.
# Changes:
# - Added optional context fields (step_id, tool_name) to AgentError or subclasses.
# - Ensured all inherit from BaseAgentError -> Exception.
# - Added AgentExecutionError subclass for executor-specific errors with context.

"""Custom exception types for the application."""

class BaseAgentError(Exception):
    """Base class for all application-specific errors."""
    def __init__(self, message: str):
        super().__init__(message)
        self.message = message

    def __str__(self) -> str:
        return self.message

class ConfigError(BaseAgentError, ValueError):
    """Exception related to configuration loading or access errors."""
    pass

class LLMError(BaseAgentError):
    """Exception related to LLM API interactions or processing."""
    def __init__(self, message: str, provider: str | None = None):
        full_message = f"LLM Error ({provider or 'unknown'}): {message}"
        super().__init__(full_message)
        self.provider = provider

class SkillError(BaseAgentError):
    """Exception raised for errors during skill execution."""
    def __init__(self, skill_name: str, message: str, original_exception: Exception | None = None):
        full_message = f"Skill '{skill_name}' failed: {message}"
        super().__init__(full_message)
        self.skill_name = skill_name
        self.original_exception = original_exception

class AgentError(BaseAgentError):
    """General exception for errors within the agent's core logic (planning, memory, etc.)."""
    pass

class AgentExecutionError(AgentError):
    """Specific exception for errors during step execution, potentially holding context."""
    def __init__(self, message: str, step_id: int | None = None, tool_name: str | None = None):
        super().__init__(message)
        self.step_id = step_id
        self.tool_name = tool_name

    def __str__(self) -> str:
        context = ""
        if self.step_id is not None:
            context += f" Step={self.step_id}"
        if self.tool_name is not None:
            context += f" Tool='{self.tool_name}'"
        return f"{self.message}{context}"


class MCPClientError(BaseAgentError):
    """Exception related to MCP client operations (connection, calls)."""
    pass

class MCPServerError(BaseAgentError):
    """Exception related to MCP server operations (lifespan, request handling)."""
    pass